﻿using Bouyei.Geo.Geometries;
using System;
using System.Collections.Generic;

namespace Bouyei.Geo.GeoParsers.EsirMdb
{
    /// <summary>
    /// mdb,gdb字节解析
    /// </summary>
    public class GeometryReader:BaseBytes
    {
        public GeoType GeometryType { get; set; }

        public Coordinate Min { get; set; }

        public Coordinate Max { get; set; }

        public GeometryReader(byte[] buffer,int offset=0)
            : base(buffer,ByteOrder.LittleEndian,offset)
        {
           
        }

        public unsafe Geometry Reader()
        {
            fixed (byte* ptr = &Buffer[0])
            {
                int type = bitExt.ToInt32(ptr);
                if (type == 1) GeometryType = GeoType.POINT;
                else if (type == 3) GeometryType = GeoType.LINESTRING;
                else if (type == 5 || type == 536870963) GeometryType = GeoType.POLYGON;
                else if (type == 8) GeometryType = GeoType.MULTIPOINT;
                else throw new Exception("geometryType not supported:" + type);

                if (GeometryType == GeoType.POINT)
                {
                    var coord = new Coordinate()
                    {
                        X = bitExt.ToDouble(ptr + 4),
                        Y = bitExt.ToDouble(ptr + 12)
                    };
                    return new Geometry(coord);
                }

                Min = new Coordinate()
                {
                    X = bitExt.ToDouble(ptr + 4),
                    Y = bitExt.ToDouble(ptr + 12)
                };
                Max = new Coordinate()
                {
                    X = bitExt.ToDouble(ptr + 20),
                    Y = bitExt.ToDouble(ptr + 28)
                };
                if (GeometryType == GeoType.MULTIPOINT)
                {
                    var coordinates = ParserToMultiPoint(ptr + 36);
                    return new Geometry(GeoType.MULTIPOINT, coordinates);
                }
                else
                {
                    var coordinates = ParserToArray(ptr + 36);
                    return new Geometry(GeometryType, coordinates);
                }
            }
        }

        private unsafe List<Coordinate[]> ParserToArray(byte* ptr)
        {
            int count = bitExt.ToInt32(ptr);
            List<Coordinate[]> coordinates = new List<Coordinate[]>(count);
            long numvertex = bitExt.ToInt64(ptr + 4);//total vertex number
            int start = 12;

            //outer ring
            int outerNumber = (int)numvertex;
            if (count <= 1)
            {
                coordinates.Add(new Coordinate[outerNumber]);
            }
            else
            {
                outerNumber = bitExt.ToInt32(ptr + start);
                start += 4;
                coordinates.Add(new Coordinate[outerNumber]);

                //inner ring
                int lastRingNum = outerNumber;
                for (int i = 0; i < count - 2; ++i)
                {
                    int ringNum = bitExt.ToInt32(ptr + start);
                    start += 4;

                    coordinates.Add(new Coordinate[ringNum - lastRingNum]);
                    lastRingNum = ringNum;
                }

                //last inner ring
                int lastNum = (int)(numvertex - lastRingNum);
                coordinates.Add(new Coordinate[lastNum]);
            }

            //coordinates 
            for (int j = 0; j < count; ++j)
            {
                var coords = coordinates[j];

                for (int i = 0; i < coords.Length; ++i)
                {
                    var lon = bitExt.ToDouble(ptr + start);
                    var lat = bitExt.ToDouble(ptr + start + 8);
                    coords[i] = new Coordinate()
                    {
                        X = lon,
                        Y = lat
                    };

                    start += 16;
                }
            }

            return coordinates;
        }

        private unsafe Coordinate[] ParserToMultiPoint(byte* ptr)
        {
            int count = bitExt.ToInt32(ptr);
            Coordinate[] coords = new Coordinate[count];
            int start = 4;

            for (int i = 0; i < count; ++i)
            {
                var lon = bitExt.ToDouble(ptr + start);
                var lat = bitExt.ToDouble(ptr + start + 8);
                coords[i] = new Coordinate()
                {
                    X = lon,
                    Y = lat
                };

                start += 16;
            }
            return coords;
        }
    }
}
